#pragma once
#include "Event.h"
#include "RunnableTask.h"
#include "PoolThreadContext.h"
#include "Runtime/Utils/LockFreeList.h"
namespace Alice
{
	class NamedPoolContext : public PoolThreadContext
	{
	public:
		virtual void ProcessTasksUntilQuit(int32 QueueIndex)
		{
			Queue(QueueIndex).QuitForReturn = false;
			do
			{
				ProcessTasksNamedThread(QueueIndex, true);
			} while (!Queue(QueueIndex).QuitForReturn && !Queue(QueueIndex).QuitForShutdown);
		}

		virtual uint64 ProcessTasksUntilIdle(int32 QueueIndex)
		{
			Queue(QueueIndex).QuitForReturn = false;
			uint64 ProcessedTasks = ProcessTasksNamedThread(QueueIndex, false);
			return ProcessedTasks;
		}
		void ProcessTasksUntilManualStop(int32 inQueueIndex)
		{
			Queue(inQueueIndex).QuitForReturn = false;
			do
			{
				ProcessTasksNamedThread(inQueueIndex, false);
				std::this_thread::sleep_for(std::chrono::milliseconds(1));
			} while (!Queue(inQueueIndex).QuitForReturn && !Queue(inQueueIndex).QuitForShutdown);
		}

		uint64 ProcessTasksNamedThread(int32 QueueIndex, bool bAllowStall)
		{
			uint64 ProcessedTasks = 0;
			const bool bIsRenderThreadMainQueue = (ExtractThreadName(mThreadName)  == ENamedThreads::ActualRenderingThread) && (QueueIndex == 0);
			while (!Queue(QueueIndex).QuitForReturn)
			{
				const bool bIsRenderThreadAndPolling = bIsRenderThreadMainQueue && (GRenderThreadPollPeriodMs >= 0);
				const bool bStallQueueAllowStall = bAllowStall && !bIsRenderThreadAndPolling;
				RunnableTask* Task = Queue(QueueIndex).StallQueue.Pop(0, bStallQueueAllowStall);
				if (!Task)
				{
					if (bAllowStall)
					{
						{
							Queue(QueueIndex).StallRestartEvent.Wait(bIsRenderThreadAndPolling ? GRenderThreadPollPeriodMs : MAX_uint32);
							if (Queue(QueueIndex).QuitForShutdown)
							{
								return ProcessedTasks;
							}
						}
						continue;
					}
					else
					{
						break; // we were asked to quit
					}
				}
				else
				{
					Task->Execute(mNewTasks, ENamedThreads((int32)mThreadName | (QueueIndex << (int32)ENamedThreads::QueueIndexShift)));
					ProcessedTasks++;
				}
			}
			return ProcessedTasks;
		}
		virtual void EnqueueFromThisThread(int32 QueueIndex, RunnableTask* Task) override
		{
			uint32 PriIndex = GetTaskPriority(Task->mThreadToExecuteOn) ? 0 : 1;
			int32 ThreadToStart = Queue(QueueIndex).StallQueue.Push(Task, PriIndex);
		}

		virtual void RequestQuit(int32 QueueIndex) override
		{
			if (QueueIndex == -1)
			{
				Queue(0).QuitForShutdown = true;
				Queue(1).QuitForShutdown = true;
				Queue(0).StallRestartEvent.TriggerAll();
				Queue(1).StallRestartEvent.TriggerAll();
			}
			else
			{
				Queue(QueueIndex).QuitForReturn = true;
			}
		}

		virtual bool EnqueueFromOtherThread(int32 QueueIndex, RunnableTask* Task) override
		{
			uint32 PriIndex = GetTaskPriority(Task->mThreadToExecuteOn) ? 0 : 1;
			int32 ThreadToStart = Queue(QueueIndex).StallQueue.Push(Task, PriIndex);

			if (ThreadToStart >= 0)
			{
				Queue(QueueIndex).StallRestartEvent.TriggerAll();
				return true;
			}
			return false;
		}

		virtual bool IsProcessingTasks(int32 QueueIndex) override
		{
			return !!Queue(QueueIndex).RecursionGuard;
		}

		virtual void WakeUp(int32 QueueIndex) override
		{
			Queue(QueueIndex).StallRestartEvent.TriggerAll();
		}

	private:
		/** Grouping of the data for an individual queue. **/
		struct ThreadTaskQueue
		{
			FStallingTaskQueue<RunnableTask, PLATFORM_CACHE_LINE_SIZE, 2> StallQueue;

			/** We need to disallow reentry of the processing loop **/
			uint32 RecursionGuard;

			/** Indicates we executed a return task, so break out of the processing loop. **/
			bool QuitForReturn;

			/** Indicates we executed a return task, so break out of the processing loop. **/
			bool QuitForShutdown;

			/** Event that this thread blocks on when it runs out of work. **/
			Event	StallRestartEvent;

			ThreadTaskQueue()
				: RecursionGuard(0)
				, QuitForReturn(false)
				, QuitForShutdown(false)
			{
			}
			~ThreadTaskQueue()
			{
			}
		};
		FORCEINLINE ThreadTaskQueue& Queue(int32 QueueIndex)
		{
			return Queues[QueueIndex];
		}
		FORCEINLINE const ThreadTaskQueue& Queue(int32 QueueIndex) const
		{
			return Queues[QueueIndex];
		}
		ThreadTaskQueue Queues[int(ENamedThreads::NumQueues)];
	};
	class AliceAnyThreadContext : public PoolThreadContext
	{
	public:
		AliceAnyThreadContext(int32 InPriorityIndex)
			: PriorityIndex(InPriorityIndex)
		{
		}
		virtual void ProcessTasksUntilQuit(int32 QueueIndex) override
		{
			do
			{
				ProcessTasks();			
			} while (!Queue.QuitForShutdown);
		}

		virtual uint64 ProcessTasksUntilIdle(int32 QueueIndex)
		{
			return 0;
		}
		virtual void RequestQuit(int32 QueueIndex)
		{
			Queue.QuitForShutdown = true;
			Queue.StallRestartEvent.TriggerAll();
		}
		virtual void WakeUp(int32 QueueIndex = 0)
		{
			Queue.StallRestartEvent.TriggerAll();
		}
		virtual bool IsProcessingTasks(int32 QueueIndex) override
		{
			return !!Queue.RecursionGuard;
		}
	private:
		uint64 ProcessTasks()
		{
			bool bCountAsStall = true;
			uint64 ProcessedTasks = 0;
			bool bDidStall = false;
			while (1)
			{
				RunnableTask* Task = FindWork();
				if (!Task)
				{
					const bool bIsMultithread = true;
					if (bIsMultithread)
					{
						Queue.StallRestartEvent.Wait(MAX_uint32);
						bDidStall = true;
					}
					if (Queue.QuitForShutdown)
					{
						break;
					}
					continue;
				}
				bDidStall = false;
				Task->Execute(mNewTasks, ENamedThreads(mThreadName));
				ProcessedTasks++;
			}
			return ProcessedTasks;
		}

		/** Grouping of the data for an individual queue. **/
		struct ThreadTaskQueue
		{
			/** Event that this thread blocks on when it runs out of work. **/
			Event StallRestartEvent;
			/** We need to disallow reentry of the processing loop **/
			uint32 RecursionGuard;
			/** Indicates we executed a return task, so break out of the processing loop. **/
			bool QuitForShutdown;

			ThreadTaskQueue()
				: RecursionGuard(0)
				, QuitForShutdown(false)
			{
			}
			~ThreadTaskQueue()
			{
			}
		};
		RunnableTask* FindWork();
		ThreadTaskQueue Queue;
		int32 PriorityIndex;
	};
}

